// 版权所有2012 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package walk

import (
	"fmt"
	"go/constant"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/reflectdata"
	"cmd/compile/internal/staticinit"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/internal/objabi"
	"cmd/internal/src"
)

// 重写树以使用单独的语句强制执行
// 求值顺序。使行走更容易，因为它可以（在此运行之后）在表达式中随意重新排序。
// 
// 如果op为/或%，则将m[k]op=r重写为m[k]=m[k]op r。
// 
// 根据运行时例程的需要引入临时变量。
// 例如，映射运行时例程引用映射键
// 因此确保所有映射键都是可寻址的
// 根据需要将它们复制到临时对象。
// 信道操作也是如此。
// 
// 安排映射索引表达式仅出现在直接
// 赋值x=m[k]或m[k]=x中，不出现在更大的表达式中。
// 
// 安排接收表达式只出现在直接赋值中
// x=<-c或作为独立语句出现<-c，不能出现在更大的表达式中。

// TODO（rsc）：多个作业期间的临时引入
// 应移到此文件中，以便可以清理临时变量
// 并且可以显式地进行OAS2FUNC和OAS2RECV 
// 节点中隐含的转换，然后清理它们的临时变量。

// TODO（rsc）：Goto和多级中断/继续可以跳过
// 插入的VARKILL注释。想办法解决这些问题。
// 当前的实现是安全的，因为它将正确执行。
// 但它不会像可能的那样积极地重用临时变量，而且
// 它可能会导致函数
// 序言中这些变量不必要地归零。

// orderState在订购过程中保持状态。
type orderState struct {
	out  []ir.Node             // 生成语句列表
	temp []*ir.Name            // 临时变量堆栈
	free map[string][]*ir.Name // 按类型列出未使用的临时变量的自由列表。LinkString（）。
	edit func(ir.Node) ir.Node // 缓存关闭o.exprNoLHS 
}

// 命令重写fn。Nbody应用文件顶部注释中描述的排序约束
// 。
func order(fn *ir.Func) {
	if base.Flag.W > 1 {
		s := fmt.Sprintf("\nbefore order %v", fn.Sym())
		ir.DumpList(s, fn.Body)
	}

	orderBlock(&fn.Body, map[string][]*ir.Name{})
}

// append typechecks stmt并将其附加到out。
func (o *orderState) append(stmt ir.Node) {
	o.out = append(o.out, typecheck.Stmt(stmt))
}

// newTemp分配一个具有给定类型的新临时文件，
// 将其推送到临时堆栈上，并返回它。
// 如果clear为true，newTemp将发出代码来将临时值归零。
func (o *orderState) newTemp(t *types.Type, clear bool) *ir.Name {
	var v *ir.Name
	key := t.LinkString()
	if a := o.free[key]; len(a) > 0 {
		v = a[len(a)-1]
		if !types.Identical(t, v.Type()) {
			base.Fatalf("expected %L to have type %v", v, t)
		}
		o.free[key] = a[:len(a)-1]
	} else {
		v = typecheck.Temp(t)
	}
	if clear {
		o.append(ir.NewAssignStmt(base.Pos, v, nil))
	}

	o.temp = append(o.temp, v)
	return v
}

// copyExpr的行为类似于newTemp，但也会发出
// 代码将临时值初始化为值n。
func (o *orderState) copyExpr(n ir.Node) *ir.Name {
	return o.copyExpr1(n, false)
}

// copyExprClear类似于copyExpr，但会在赋值之前清除临时值。
// 当tmp=n的求值变成
// 一个函数调用时使用，该函数调用被传递一个指向临时值的指针作为输出空间。
// 如果在编写tmp之前调用被阻塞，
// 垃圾收集器仍将临时处理为活动，
// 因此我们必须在进入该调用之前将其归零。
// 今天，这种情况只发生在通道接收操作中。
// （另一个候选者是map access，但map access 
// 返回指向结果数据的指针，而不是使用要填写的指针
// 。）
func (o *orderState) copyExprClear(n ir.Node) *ir.Name {
	return o.copyExpr1(n, true)
}

func (o *orderState) copyExpr1(n ir.Node, clear bool) *ir.Name {
	t := n.Type()
	v := o.newTemp(t, clear)
	o.append(ir.NewAssignStmt(base.Pos, v, n))
	return v
}

// cheapExpr返回n的廉价版本。
// 廉价的定义是n是一个变量或常量。
// 如果没有，cheapExpr分配一个新的tmp，发出tmp=n，
// 然后返回tmp。
func (o *orderState) cheapExpr(n ir.Node) ir.Node {
	if n == nil {
		return nil
	}

	switch n.Op() {
	case ir.ONAME, ir.OLITERAL, ir.ONIL:
		return n
	case ir.OLEN, ir.OCAP:
		n := n.(*ir.UnaryExpr)
		l := o.cheapExpr(n.X)
		if l == n.X {
			return n
		}
		a := ir.SepCopy(n).(*ir.UnaryExpr)
		a.X = l
		return typecheck.Expr(a)
	}

	return o.copyExpr(n)
}

// safeExpr返回n的安全版本。
// safe的定义是n可以多次出现
// ，而不违反原始程序的语义，
// 而分配给安全版本的效果与分配给原始n的效果相同。
// 
// 预期用途是在将x+=y重写为x=x+y时应用于x。
func (o *orderState) safeExpr(n ir.Node) ir.Node {
	switch n.Op() {
	case ir.ONAME, ir.OLITERAL, ir.ONIL:
		return n

	case ir.OLEN, ir.OCAP:
		n := n.(*ir.UnaryExpr)
		l := o.safeExpr(n.X)
		if l == n.X {
			return n
		}
		a := ir.SepCopy(n).(*ir.UnaryExpr)
		a.X = l
		return typecheck.Expr(a)

	case ir.ODOT:
		n := n.(*ir.SelectorExpr)
		l := o.safeExpr(n.X)
		if l == n.X {
			return n
		}
		a := ir.SepCopy(n).(*ir.SelectorExpr)
		a.X = l
		return typecheck.Expr(a)

	case ir.ODOTPTR:
		n := n.(*ir.SelectorExpr)
		l := o.cheapExpr(n.X)
		if l == n.X {
			return n
		}
		a := ir.SepCopy(n).(*ir.SelectorExpr)
		a.X = l
		return typecheck.Expr(a)

	case ir.ODEREF:
		n := n.(*ir.StarExpr)
		l := o.cheapExpr(n.X)
		if l == n.X {
			return n
		}
		a := ir.SepCopy(n).(*ir.StarExpr)
		a.X = l
		return typecheck.Expr(a)

	case ir.OINDEX, ir.OINDEXMAP:
		n := n.(*ir.IndexExpr)
		var l ir.Node
		if n.X.Type().IsArray() {
			l = o.safeExpr(n.X)
		} else {
			l = o.cheapExpr(n.X)
		}
		r := o.cheapExpr(n.Index)
		if l == n.X && r == n.Index {
			return n
		}
		a := ir.SepCopy(n).(*ir.IndexExpr)
		a.X = l
		a.Index = r
		return typecheck.Expr(a)

	default:
		base.Fatalf("order.safeExpr %v", n.Op())
		return nil // 未达到
	}
}

// IsAddressOk报告是否可以将n的地址传递给运行时例程。
// 获取变量的地址会使活动性和优化分析
// 不知道变量的生命周期结束在哪里。为了避免影响对普通堆栈变量的分析，这些变量不是“isaddrook”。临时工是可以的，因为我们发出明确的VARKILL指令，标志着临时工生命的结束。
func isaddrokay(n ir.Node) bool {
	return ir.IsAddressable(n) && (n.Op() != ir.ONAME || n.(*ir.Name).Class == ir.PEXTERN || ir.IsAutoTmp(n))
}

// addrTemp确保n可以通过地址传递给运行时例程。
// 如果原始参数n不正确，addrTemp将创建一个tmp，并发出
// tmp=n，然后返回tmp。
// addrTemp的结果必须重新分配给n，例如
// n.Left=o.addrTemp（n.Left）
func (o *orderState) addrTemp(n ir.Node) ir.Node {
	if n.Op() == ir.OLITERAL || n.Op() == ir.ONIL {
		// TODO:将其扩展到所有静态复合文本节点？
		n = typecheck.DefaultLit(n, nil)
		types.CalcSize(n.Type())
		vstat := readonlystaticname(n.Type())
		var s staticinit.Schedule
		s.StaticAssign(vstat, 0, n, n.Type())
		if s.Out != nil {
			base.Fatalf("staticassign of const generated code: %+v", n)
		}
		vstat = typecheck.Expr(vstat).(*ir.Name)
		return vstat
	}
	if isaddrokay(n) {
		return n
	}
	return o.copyExpr(n)
}

// mapKeyTemp将n准备为映射运行时调用中的键，并返回n。
// 它应仅用于具有*_fast*版本的映射运行时调用。
func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
	// 大多数map调用需要获取密钥的地址。
	// 异常：map*_fast*调用。见戈朗。org/issue/19015。
	alg := mapfast(t)
	if alg == mapslow {
		return o.addrTemp(n)
	}
	var kt *types.Type
	switch alg {
	case mapfast32:
		kt = types.Types[types.TUINT32]
	case mapfast64:
		kt = types.Types[types.TUINT64]
	case mapfast32ptr, mapfast64ptr:
		kt = types.Types[types.TUNSAFEPTR]
	case mapfaststr:
		kt = types.Types[types.TSTRING]
	}
	nt := n.Type()
	switch {
	case nt == kt:
		return n
	case nt.Kind() == kt.Kind(), nt.IsPtrShaped() && kt.IsPtrShaped():
		// 可以直接转换（例如，命名类型转换为基础类型，或一个指针指向另一个类型）
		return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONVNOP, kt, n))
	case nt.IsInteger() && kt.IsInteger():
		// 可以直接转换（例如，int32转换为uint32）
		if n.Op() == ir.OLITERAL && nt.IsSigned() {
			// 避免持续溢出错误
			n = ir.NewConstExpr(constant.MakeUint64(uint64(ir.Int64Val(n))), n)
			n.SetType(kt)
			return n
		}
		return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONV, kt, n))
	default:
		// 不安全的内存强制转换。
		// 我们需要使用kt型进行加载。创建一个kt到
		// /类型的临时模板，确保充分对齐。nt可能未对齐。
		if uint8(kt.Alignment()) < uint8(nt.Alignment()) {
			base.Fatalf("mapKeyTemp: key type is not sufficiently aligned, kt=%v nt=%v", kt, nt)
		}
		tmp := o.newTemp(kt, true)
		// /*（*nt）（&tmp）=n 
		var e ir.Node = typecheck.NodAddr(tmp)
		e = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, nt.PtrTo(), e)
		e = ir.NewStarExpr(n.Pos(), e)
		o.append(ir.NewAssignStmt(base.Pos, e, n))
		return tmp
	}
}

// /mapKeyReplaceStrConv将OBYTES2STR替换为OBYTES2STRTMP 
// /n，以避免在地图查找中为键分配字符串。
// 返回一个布尔值，指示是否进行了修改。
// 
// For:
// x=m[string（k）]
// x=m[T1{…Tn{…string（k），…]
// 其中k是[]字节，T1到Tn是结构和数组文本的嵌套，
// 通过重用[]字节的备份数组，可以避免为字符串分配备份字节。这些是特殊情况
// 用于在将字节片转换为字符串时避免分配。
// 一般情况下处理这些是很好的，但由于
// /[]字节键在映射中是不允许的，所以在实践中，在重要的情况下会出现使用字符串（k）
// 的情况。见第3512期。
func mapKeyReplaceStrConv(n ir.Node) bool {
	var replaced bool
	switch n.Op() {
	case ir.OBYTES2STR:
		n := n.(*ir.ConvExpr)
		n.SetOp(ir.OBYTES2STRTMP)
		replaced = true
	case ir.OSTRUCTLIT:
		n := n.(*ir.CompLitExpr)
		for _, elem := range n.List {
			elem := elem.(*ir.StructKeyExpr)
			if mapKeyReplaceStrConv(elem.Value) {
				replaced = true
			}
		}
	case ir.OARRAYLIT:
		n := n.(*ir.CompLitExpr)
		for _, elem := range n.List {
			if elem.Op() == ir.OKEY {
				elem = elem.(*ir.KeyExpr).Value
			}
			if mapKeyReplaceStrConv(elem) {
				replaced = true
			}
		}
	}
	return replaced
}

type ordermarker int

// markTemp返回临时变量堆栈的顶部。
func (o *orderState) markTemp() ordermarker {
	return ordermarker(len(o.temp))
}

// popTemp从堆栈中弹出临时值，直到达到标记
// ，该标记必须由markTemp返回。
func (o *orderState) popTemp(mark ordermarker) {
	for _, n := range o.temp[mark:] {
		key := n.Type().LinkString()
		o.free[key] = append(o.free[key], n)
	}
	o.temp = o.temp[:mark]
}

// cleantempnoop为临时堆栈上标记上方的每个临时堆栈发出VARKILL指令，以*out 
// 。
// 它不会从堆栈中弹出临时值。
func (o *orderState) cleanTempNoPop(mark ordermarker) []ir.Node {
	var out []ir.Node
	for i := len(o.temp) - 1; i >= int(mark); i-- {
		n := o.temp[i]
		out = append(out, typecheck.Stmt(ir.NewUnaryExpr(base.Pos, ir.OVARKILL, n)))
	}
	return out
}

// cleanTemp为临时堆栈上
// 标记上方的每个临时对象发出VARKILL指令，并将其从堆栈中移除。
func (o *orderState) cleanTemp(top ordermarker) {
	o.out = append(o.out, o.cleanTempNoPop(top)...)
	o.popTemp(top)
}

// stmtList对列表中的每个语句进行排序。
func (o *orderState) stmtList(l ir.Nodes) {
	s := l
	for i := range s {
		orderMakeSliceCopy(s[i:])
		o.stmt(s[i])
	}
}

// orderMakeSicleCopy匹配模式：
// m=OMAKESLICE（[]T，x）；OCOPY（m，s）
// 并将其重写为：
// m=OMAKESLICECOPY（[]T，x，s）；nil 
func orderMakeSliceCopy(s []ir.Node) {
	if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
		return
	}
	if len(s) < 2 || s[0] == nil || s[0].Op() != ir.OAS || s[1] == nil || s[1].Op() != ir.OCOPY {
		return
	}

	as := s[0].(*ir.AssignStmt)
	cp := s[1].(*ir.BinaryExpr)
	if as.Y == nil || as.Y.Op() != ir.OMAKESLICE || ir.IsBlank(as.X) ||
		as.X.Op() != ir.ONAME || cp.X.Op() != ir.ONAME || cp.Y.Op() != ir.ONAME ||
		as.X.Name() != cp.X.Name() || cp.X.Name() == cp.Y.Name() {
		// 这一行上面有不同的相等运算符：
		// 我们想要as。X和cp.X是同一个名称，
		// 但我们希望初始数据来自不同的名称。
		return
	}

	mk := as.Y.(*ir.MakeExpr)
	if mk.Esc() == ir.EscNone || mk.Len == nil || mk.Cap != nil {
		return
	}
	mk.SetOp(ir.OMAKESLICECOPY)
	mk.Cap = cp.Y
	// 当m=OMAKESLICE（[]T，len（s））时集有界；OCOPY（m，s）
	mk.SetBounded(mk.Len.Op() == ir.OLEN && ir.SameSafeExpr(mk.Len.(*ir.UnaryExpr).X, cp.Y))
	as.Y = typecheck.Expr(mk)
	s[1] = nil // 删除单独的拷贝调用
}

// 用于libfuzzer的边缘插入覆盖检测。
func (o *orderState) edge() {
	if base.Debug.Libfuzzer == 0 {
		return
	}

	// 创建一个新的uint8计数器，以便在
	// /uu libfuzzer_extra_counters部分中分配。
	counter := staticinit.StaticName(types.Types[types.TUINT8])
	counter.SetLibfuzzerExtraCounter(true)
	// 除了设置SetLibfuzzerExtraCounter之外，我们还预先将
	// 符号类型设置为SLIBFUZZER_EXTRA_计数器，以便种族检测器
	// 检测过程（它没有访问
	// SetLibfuzzerExtraCounter设置的标志）知道忽略它们。此信息在到达编译步骤时丢失，因此SetLibfuzzerExtraCounter 
	// 仍然是必需的。
	counter.Linksym().Type = objabi.SLIBFUZZER_EXTRA_COUNTER

	// 计数器+=1 
	incr := ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1))
	o.append(incr)
}

// orderBlock将n中的语句块排序为一个新切片，
// 然后用新切片替换n中的旧切片。
// free是一个映射，可用于按类型获取临时变量。
func orderBlock(n *ir.Nodes, free map[string][]*ir.Name) {
	var order orderState
	order.free = free
	mark := order.markTemp()
	order.edge()
	order.stmtList(*n)
	order.cleanTemp(mark)
	*n = order.out
}

// exprInPlace在*np和
// 中将副作用列为最终*np的初始列表。
// exprInPlace的结果必须重新分配给n，例如
// n.Left=o.exprInPlace（n.Left）
func (o *orderState) exprInPlace(n ir.Node) ir.Node {
	var order orderState
	order.free = o.free
	n = order.expr(n, nil)
	n = ir.InitExpr(order.out, n)

	// 在外部列表的开头插入新的临时表。
	o.temp = append(o.temp, order.temp...)
	return n
}

// orderStmtInPlace命令单个语句的副作用*np 
// 并用生成的语句列表替换它。
// orderStmtInPlace的结果必须分配回n，例如
// n.Left=orderStmtInPlace（n.Left）
// free是一个映射，可用于按类型获取临时变量。
func orderStmtInPlace(n ir.Node, free map[string][]*ir.Name) ir.Node {
	var order orderState
	order.free = free
	mark := order.markTemp()
	order.stmt(n)
	order.cleanTemp(mark)
	return ir.NewBlockStmt(src.NoXPos, order.out)
}

// init将n的init列表移动到o.out。
func (o *orderState) init(n ir.Node) {
	if ir.MayBeShared(n) {
		// 为了并发安全，不要变异潜在的共享节点。
		// 首先，确保这里不需要任何工作。
		if len(n.Init()) > 0 {
			base.Fatalf("order.init shared node with ninit")
		}
		return
	}
	o.stmtList(ir.TakeInit(n))
}

// call命令调用表达式n.
// n.Op是OCALLFUNC/ocalliner或类似occopy的内置函数。
func (o *orderState) call(nn ir.Node) {
	if len(nn.Init()) > 0 {
		// 调用者应该已经调用了o.init（nn）。
		base.Fatalf("%v with unexpected ninit", nn.Op())
	}
	if nn.Op() == ir.OCALLMETH {
		base.FatalfAt(nn.Pos(), "OCALLMETH missed by typecheck")
	}

	// 内置函数。
	if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLINTER {
		switch n := nn.(type) {
		default:
			base.Fatalf("unexpected call: %+v", n)
		case *ir.UnaryExpr:
			n.X = o.expr(n.X, nil)
		case *ir.ConvExpr:
			n.X = o.expr(n.X, nil)
		case *ir.BinaryExpr:
			n.X = o.expr(n.X, nil)
			n.Y = o.expr(n.Y, nil)
		case *ir.MakeExpr:
			n.Len = o.expr(n.Len, nil)
			n.Cap = o.expr(n.Cap, nil)
		case *ir.CallExpr:
			o.exprList(n.Args)
		}
		return
	}

	n := nn.(*ir.CallExpr)
	typecheck.FixVariadicCall(n)

	if isFuncPCIntrinsic(n) && isIfaceOfFunc(n.Args[0]) {
		// 用于内部/abi。FuncPCABIxxx（fn），如果fn是一个已定义的函数，则
		// 此处不引入临时变量，因此稍后在walk中将其重写为符号地址引用更容易。
		return
	}

	n.X = o.expr(n.X, nil)
	o.exprList(n.Args)
}

// mapAssign将n附加到o.out。
func (o *orderState) mapAssign(n ir.Node) {
	switch n.Op() {
	default:
		base.Fatalf("order.mapAssign %v", n.Op())

	case ir.OAS:
		n := n.(*ir.AssignStmt)
		if n.X.Op() == ir.OINDEXMAP {
			n.Y = o.safeMapRHS(n.Y)
		}
		o.out = append(o.out, n)
	case ir.OASOP:
		n := n.(*ir.AssignOpStmt)
		if n.X.Op() == ir.OINDEXMAP {
			n.Y = o.safeMapRHS(n.Y)
		}
		o.out = append(o.out, n)
	}
}

func (o *orderState) safeMapRHS(r ir.Node) ir.Node {
	// 确保我们在开始地图插入之前评估RHS。
	// 我们需要确保RHS不会惊慌失措。见第22881期。
	if r.Op() == ir.OAPPEND {
		r := r.(*ir.CallExpr)
		s := r.Args[1:]
		for i, n := range s {
			s[i] = o.cheapExpr(n)
		}
		return r
	}
	return o.cheapExpr(r)
}

// stmt命令语句n，附加在o.out之后。
// 尽可能使用VARKILL指令清除语句期间创建的临时变量。
func (o *orderState) stmt(n ir.Node) {
	if n == nil {
		return
	}

	lno := ir.SetPos(n)
	o.init(n)

	switch n.Op() {
	default:
		base.Fatalf("order.stmt %v", n.Op())

	case ir.OVARKILL, ir.OVARLIVE, ir.OINLMARK:
		o.out = append(o.out, n)

	case ir.OAS:
		n := n.(*ir.AssignStmt)
		t := o.markTemp()
		n.X = o.expr(n.X, nil)
		n.Y = o.expr(n.Y, n.X)
		o.mapAssign(n)
		o.cleanTemp(t)

	case ir.OASOP:
		n := n.(*ir.AssignOpStmt)
		t := o.markTemp()
		n.X = o.expr(n.X, nil)
		n.Y = o.expr(n.Y, nil)

		if base.Flag.Cfg.Instrumenting || n.X.Op() == ir.OINDEXMAP && (n.AsOp == ir.ODIV || n.AsOp == ir.OMOD) {
			// 将m[k]op=r重写为m[k]=m[k]op r，这样我们就可以确保如果op恐慌，因为r为零，恐慌发生在map赋值之前。
			// 深度复制在这里是个大问题，但safeExpr 
			// 确保没有太深的内容被复制。
			l1 := o.safeExpr(n.X)
			l2 := ir.DeepCopy(src.NoXPos, l1)
			if l2.Op() == ir.OINDEXMAP {
				l2 := l2.(*ir.IndexExpr)
				l2.Assigned = false
			}
			l2 = o.copyExpr(l2)
			r := o.expr(typecheck.Expr(ir.NewBinaryExpr(n.Pos(), n.AsOp, l2, n.Y)), nil)
			as := typecheck.Stmt(ir.NewAssignStmt(n.Pos(), l1, r))
			o.mapAssign(as)
			o.cleanTemp(t)
			return
		}

		o.mapAssign(n)
		o.cleanTemp(t)

	case ir.OAS2:
		n := n.(*ir.AssignListStmt)
		t := o.markTemp()
		o.exprList(n.Lhs)
		o.exprList(n.Rhs)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	// 特殊：避免复制func调用n。右
	case ir.OAS2FUNC:
		n := n.(*ir.AssignListStmt)
		t := o.markTemp()
		o.exprList(n.Lhs)
		call := n.Rhs[0]
		o.init(call)
		if ic, ok := call.(*ir.InlinedCallExpr); ok {
			o.stmtList(ic.Body)

			n.SetOp(ir.OAS2)
			n.Rhs = ic.ReturnVars

			o.exprList(n.Rhs)
			o.out = append(o.out, n)
		} else {
			o.call(call)
			o.as2func(n)
		}
		o.cleanTemp(t)

	// 特殊：使用临时变量保存结果，
	// 以便运行时可以获取临时调用的地址。
	// 无临时空白作业。
	// 
	// OAS2MAPR：确保密钥在需要时可寻址，
	// 并确保OINDEXMAP未被复制。
	case ir.OAS2DOTTYPE, ir.OAS2RECV, ir.OAS2MAPR:
		n := n.(*ir.AssignListStmt)
		t := o.markTemp()
		o.exprList(n.Lhs)

		switch r := n.Rhs[0]; r.Op() {
		case ir.ODOTTYPE2:
			r := r.(*ir.TypeAssertExpr)
			r.X = o.expr(r.X, nil)
		case ir.ODYNAMICDOTTYPE2:
			r := r.(*ir.DynamicTypeAssertExpr)
			r.X = o.expr(r.X, nil)
			r.T = o.expr(r.T, nil)
		case ir.ORECV:
			r := r.(*ir.UnaryExpr)
			r.X = o.expr(r.X, nil)
		case ir.OINDEXMAP:
			r := r.(*ir.IndexExpr)
			r.X = o.expr(r.X, nil)
			r.Index = o.expr(r.Index, nil)
			// 请参见下面OINDEXMAP的类似转换。
			_ = mapKeyReplaceStrConv(r.Index)
			r.Index = o.mapKeyTemp(r.X.Type(), r.Index)
		default:
			base.Fatalf("order.stmt: %v", r.Op())
		}

		o.as2ok(n)
		o.cleanTemp(t)

	// 特殊：不将n保存到out。
	case ir.OBLOCK:
		n := n.(*ir.BlockStmt)
		o.stmtList(n.List)

	// Special:n->left不是一个表达式；按原样保存。
	case ir.OBREAK,
		ir.OCONTINUE,
		ir.ODCL,
		ir.ODCLCONST,
		ir.ODCLTYPE,
		ir.OFALL,
		ir.OGOTO,
		ir.OLABEL,
		ir.OTAILCALL:
		o.out = append(o.out, n)

	// 特殊：处理调用参数。
	case ir.OCALLFUNC, ir.OCALLINTER:
		n := n.(*ir.CallExpr)
		t := o.markTemp()
		o.call(n)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.OINLCALL:
		n := n.(*ir.InlinedCallExpr)
		o.stmtList(n.Body)

		// 放弃结果；仔细检查是否没有副作用
		for _, result := range n.ReturnVars {
			if staticinit.AnySideEffects(result) {
				base.FatalfAt(result.Pos(), "inlined call result has side effects: %v", result)
			}
		}

	case ir.OCHECKNIL, ir.OCLOSE, ir.OPANIC, ir.ORECV:
		n := n.(*ir.UnaryExpr)
		t := o.markTemp()
		n.X = o.expr(n.X, nil)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.OCOPY:
		n := n.(*ir.BinaryExpr)
		t := o.markTemp()
		n.X = o.expr(n.X, nil)
		n.Y = o.expr(n.Y, nil)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.OPRINT, ir.OPRINTN, ir.ORECOVERFP:
		n := n.(*ir.CallExpr)
		t := o.markTemp()
		o.call(n)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	// 特殊：将参数排序为内部调用，但不将其自身调用。
	case ir.ODEFER, ir.OGO:
		n := n.(*ir.GoDeferStmt)
		t := o.markTemp()
		o.init(n.Call)
		o.call(n.Call)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.ODELETE:
		n := n.(*ir.CallExpr)
		t := o.markTemp()
		n.Args[0] = o.expr(n.Args[0], nil)
		n.Args[1] = o.expr(n.Args[1], nil)
		n.Args[1] = o.mapKeyTemp(n.Args[0].Type(), n.Args[1])
		o.out = append(o.out, n)
		o.cleanTemp(t)

	// 在
	// 循环体的开头和for语句的后面清除条件评估中的临时值。
	case ir.OFOR:
		n := n.(*ir.ForStmt)
		t := o.markTemp()
		n.Cond = o.exprInPlace(n.Cond)
		n.Body.Prepend(o.cleanTempNoPop(t)...)
		orderBlock(&n.Body, o.free)
		n.Post = orderStmtInPlace(n.Post, o.free)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	// 清除两个分支开始处的临时状态。
	case ir.OIF:
		n := n.(*ir.IfStmt)
		t := o.markTemp()
		n.Cond = o.exprInPlace(n.Cond)
		n.Body.Prepend(o.cleanTempNoPop(t)...)
		n.Else.Prepend(o.cleanTempNoPop(t)...)
		o.popTemp(t)
		orderBlock(&n.Body, o.free)
		orderBlock(&n.Else, o.free)
		o.out = append(o.out, n)

	case ir.ORANGE:
		// n.正确的是被覆盖的表达式。
		// 订购，如果需要，请复印一份。
		// 我们几乎总是这样做，以确保我们不会看到循环过程中的任何值更改。
		// 通常拷贝很便宜（例如，数组指针、
		// chan、slice、string都很小）。
		// 异常的范围是数组值
		// （不是一个片，不是指向数组的指针），
		// 必须进行复制，以避免在
		// 范围体中看到更新。不过，在数组值上进行测距并不常见。

		// 标记[]字节（str）范围表达式以重用字符串备份存储。
		// 它是安全的，因为存储不能变异。
		n := n.(*ir.RangeStmt)
		if n.X.Op() == ir.OSTR2BYTES {
			n.X.(*ir.ConvExpr).SetOp(ir.OSTR2BYTESTMP)
		}

		t := o.markTemp()
		n.X = o.expr(n.X, nil)

		orderBody := true
		xt := typecheck.RangeExprType(n.X.Type())
		switch xt.Kind() {
		default:
			base.Fatalf("order.stmt range %v", n.Type())

		case types.TARRAY, types.TSLICE:
			if n.Value == nil || ir.IsBlank(n.Value) {
				// 对于i:=范围x将只使用x一次来计算len（x）。
				// 无需复制。
				break
			}
			fallthrough

		case types.TCHAN, types.TSTRING:
			// chan、string、slice、array范围多次使用值。
			// 复制。
			r := n.X

			if r.Type().IsString() && r.Type() != types.Types[types.TSTRING] {
				r = ir.NewConvExpr(base.Pos, ir.OCONV, nil, r)
				r.SetType(types.Types[types.TSTRING])
				r = typecheck.Expr(r)
			}

			n.X = o.copyExpr(r)

		case types.TMAP:
			if isMapClear(n) {
				// 保留地图主体的清晰图案，以便在步行时检测到它。当优化运行时调用的范围循环时，循环体将不被使用。
				orderBody = false
				break
			}

			// 复制映射值，以防它是映射文字。
			// TODO（rsc）：使tmp=文本表达式重用tmp。
			// 对于地图来说，tmp只是一个词，所以它几乎不重要。
			r := n.X
			n.X = o.copyExpr(r)

			// n.Prealloc是迭代器的临时值。
			// MapIterType包含指针，需要归零。
			n.Prealloc = o.newTemp(reflectdata.MapIterType(xt), true)
		}
		n.Key = o.exprInPlace(n.Key)
		n.Value = o.exprInPlace(n.Value)
		if orderBody {
			orderBlock(&n.Body, o.free)
		}
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.ORETURN:
		n := n.(*ir.ReturnStmt)
		o.exprList(n.Results)
		o.out = append(o.out, n)

	// 特殊：在每个区块条目中清除大小写临时值。
	// Select必须输入它的一个块，所以最后不需要清洗。
	// 双重特殊：select的求值顺序比普通表达式更严格。甚至像p.c 
	// 这样的东西也必须被提升到临时状态，这样它就不能在不同
	// 情况下的通道评估后重新排序（如果p为零，那么故障的时间将
	// 泄露）。
	case ir.OSELECT:
		n := n.(*ir.SelectStmt)
		t := o.markTemp()
		for _, ncas := range n.Cases {
			r := ncas.Comm
			ir.SetPos(ncas)

			// 为ninit添加任何新的正文序言。
			// 下一个循环将把ninit插入nbody。
			if len(ncas.Init()) != 0 {
				base.Fatalf("order select ninit")
			}
			if r == nil {
				continue
			}
			switch r.Op() {
			default:
				ir.Dump("select case", r)
				base.Fatalf("unknown op in select %v", r.Op())

			case ir.OSELRECV2:
				// case x，ok=<-c 
				r := r.(*ir.AssignListStmt)
				recv := r.Rhs[0].(*ir.UnaryExpr)
				recv.X = o.expr(recv.X, nil)
				if !ir.IsAutoTmp(recv.X) {
					recv.X = o.copyExpr(recv.X)
				}
				init := ir.TakeInit(r)

				colas := r.Def
				do := func(i int, t *types.Type) {
					n := r.Lhs[i]
					if ir.IsBlank(n) {
						return
					}
					// 如果这是case x:=<-ch或case x，y:=<-ch，那么这个case有
					// ODCL节点来声明x和y。我们希望将
					// 声明（以及可能的分配）延迟到case body内部。
					// 删除此处的ODCL节点，并在下面的正文中重新创建它们。
					if colas {
						if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).X == n {
							init = init[1:]

							// iimport可能添加了默认的初始化赋值
							// 这是因为它处理ODCL语句的方式。
							if len(init) > 0 && init[0].Op() == ir.OAS && init[0].(*ir.AssignStmt).X == n {
								init = init[1:]
							}
						}
						dcl := typecheck.Stmt(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
						ncas.PtrInit().Append(dcl)
					}
					tmp := o.newTemp(t, t.HasPointers())
					as := typecheck.Stmt(ir.NewAssignStmt(base.Pos, n, typecheck.Conv(tmp, n.Type())))
					ncas.PtrInit().Append(as)
					r.Lhs[i] = tmp
				}
				do(0, recv.X.Type().Elem())
				do(1, types.Types[types.TBOOL])
				if len(init) != 0 {
					ir.DumpList("ninit", r.Init())
					base.Fatalf("ninit on select recv")
				}
				orderBlock(ncas.PtrInit(), o.free)

			case ir.OSEND:
				r := r.(*ir.SendStmt)
				if len(r.Init()) != 0 {
					ir.DumpList("ninit", r.Init())
					base.Fatalf("ninit on select send")
				}

				// 案例c<-x 
				// r->左边是c，r->右边是x，这两种情况都会被计算。
				r.Chan = o.expr(r.Chan, nil)

				if !ir.IsAutoTmp(r.Chan) {
					r.Chan = o.copyExpr(r.Chan)
				}
				r.Value = o.expr(r.Value, nil)
				if !ir.IsAutoTmp(r.Value) {
					r.Value = o.copyExpr(r.Value)
				}
			}
		}
		// 现在我们已经积累了所有的临时物品，请清理它们。
		// 也插入上一个循环中排队的任何ninit。
		// /（临时清洁必须遵循ninit工作流程。）这真的有必要吗？
		for _, cas := range n.Cases {
			orderBlock(&cas.Body, o.free)
			cas.Body.Prepend(o.cleanTempNoPop(t)...)

			// walkSelect显示为行走Ninit。
			cas.Body.Prepend(ir.TakeInit(cas)...)
		}

		o.out = append(o.out, n)
		o.popTemp(t)

	// 特殊：发送的值作为指针传递；使其可寻址。
	case ir.OSEND:
		n := n.(*ir.SendStmt)
		t := o.markTemp()
		n.Chan = o.expr(n.Chan, nil)
		n.Value = o.expr(n.Value, nil)
		if base.Flag.Cfg.Instrumenting {
			// 强制复制到堆栈中，以便（chan T）（nil）<-x 
			// 仍作为x的读取进行检测。
			n.Value = o.copyExpr(n.Value)
		} else {
			n.Value = o.addrTemp(n.Value)
		}
		o.out = append(o.out, n)
		o.cleanTemp(t)

	// TODO（rsc）：更积极地清理临时文件。
	// 请注意，因为walkSwitch将把一些
	// 开关重写为二进制搜索，所以这并不像看上去那么容易。
	// （如果我们在这里运行该代码，我们可以在
	// /的If-else链上调用order.stmt。）
	// 现在只需清理最后的所有临时文件。
	// 实际上没关系。
	case ir.OSWITCH:
		n := n.(*ir.SwitchStmt)
		if base.Debug.Libfuzzer != 0 && !hasDefaultCase(n) {
			// 添加空的“默认值：”用于检测的案例。
			n.Cases = append(n.Cases, ir.NewCaseStmt(base.Pos, nil, nil))
		}

		t := o.markTemp()
		n.Tag = o.expr(n.Tag, nil)
		for _, ncas := range n.Cases {
			o.exprListInPlace(ncas.List)
			orderBlock(&ncas.Body, o.free)
		}

		o.out = append(o.out, n)
		o.cleanTemp(t)
	}

	base.Pos = lno
}

func hasDefaultCase(n *ir.SwitchStmt) bool {
	for _, ncas := range n.Cases {
		if len(ncas.List) == 0 {
			return true
		}
	}
	return false
}

// exprList将表达式列表l排序为o。
func (o *orderState) exprList(l ir.Nodes) {
	s := l
	for i := range s {
		s[i] = o.expr(s[i], nil)
	}
}

// exprlistin将表达式列表l排序，但保存
// 对单个表达式列表的副作用。
func (o *orderState) exprListInPlace(l ir.Nodes) {
	s := l
	for i := range s {
		s[i] = o.exprInPlace(s[i])
	}
}

func (o *orderState) exprNoLHS(n ir.Node) ir.Node {
	return o.expr(n, nil)
}

// expr命令一个表达式，根据需要在o.out中添加边
// effects。
// 如果这是赋值lhs=*np的一部分，则给出lhs。
// 否则lhs==nil。（当lhs！=nil时，可以使用
// 来避免将表达式的结果复制到临时值。）
// expr的结果必须重新分配给n，例如
// n.Left=o.expr（n.Left，lhs）
func (o *orderState) expr(n, lhs ir.Node) ir.Node {
	if n == nil {
		return n
	}
	lno := ir.SetPos(n)
	n = o.expr1(n, lhs)
	base.Pos = lno
	return n
}

func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
	o.init(n)

	switch n.Op() {
	default:
		if o.edit == nil {
			o.edit = o.exprNoLHS // 一旦
		}
		ir.EditChildren(n, o.edit)
		return n

	// 添加字符串变成函数调用，就创建闭包。
	// 分配一个临时文件来保存字符串。
	// 使用直接运行时帮助程序的字符串少于5个。
	case ir.OADDSTR:
		n := n.(*ir.AddStringExpr)
		o.exprList(n.List)

		if len(n.List) > 5 {
			t := types.NewArray(types.Types[types.TSTRING], int64(len(n.List)))
			n.Prealloc = o.newTemp(t, false)
		}

		// 标记字符串（字节片）参数，以便在转换期间重用支持
		// 缓冲区的字节片。字符串连接不
		// 记住字符串以备以后使用，因此是安全的。
		// 但是，我们只能在至少有一个非空字符串文本的情况下才能这样做。
		// 否则，如果所有其他参数都是空字符串，
		// concatstrings将向调用者返回对临时字符串
		// 的引用。
		hasbyte := false

		haslit := false
		for _, n1 := range n.List {
			hasbyte = hasbyte || n1.Op() == ir.OBYTES2STR
			haslit = haslit || n1.Op() == ir.OLITERAL && len(ir.StringVal(n1)) != 0
		}

		if haslit && hasbyte {
			for _, n2 := range n.List {
				if n2.Op() == ir.OBYTES2STR {
					n2 := n2.(*ir.ConvExpr)
					n2.SetOp(ir.OBYTES2STRTMP)
				}
			}
		}
		return n

	case ir.OINDEXMAP:
		n := n.(*ir.IndexExpr)
		n.X = o.expr(n.X, nil)
		n.Index = o.expr(n.Index, nil)
		needCopy := false

		if !n.Assigned {
			// 强制执行我们没有复制的任何[]字节片
			// 不能在映射索引之前更改，方法是强制
			// 映射索引在
			// 转换之后立即发生。请参阅下面几行的copyExpr。
			needCopy = mapKeyReplaceStrConv(n.Index)

			if base.Flag.Cfg.Instrumenting {
				// 种族检测器需要副本。
				needCopy = true
			}
		}

		// 键必须是可寻址的
		n.Index = o.mapKeyTemp(n.X.Type(), n.Index)
		if needCopy {
			return o.copyExpr(n)
		}
		return n

	// 具体类型（非接口）参数可能需要可寻址的
	// 临时参数才能传递到运行时转换例程。
	case ir.OCONVIFACE, ir.OCONVIDATA:
		n := n.(*ir.ConvExpr)
		n.X = o.expr(n.X, nil)
		if n.X.Type().IsInterface() {
			return n
		}
		if _, _, needsaddr := dataWordFuncName(n.X.Type()); needsaddr || isStaticCompositeLiteral(n.X) {
			// 如果我们需要将地址传递给转换函数，需要一个临时值。
			// 我们也在这里处理静态复合文字节点，生成一个命名的静态全局
			// 我们可以直接将其地址放入一个接口中（请参阅walk中的oconvidace/OCONVIDATA案例）。
			n.X = o.addrTemp(n.X)
		}
		return n

	case ir.OCONVNOP:
		n := n.(*ir.ConvExpr)
		if n.X.Op() == ir.OCALLMETH {
			base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
		}
		if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER) {
			call := n.X.(*ir.CallExpr)
			// 重新订购时不安全。将指针（f（））插入一个单独的
			// 语句中，转换和函数调用必须保持
			// 在一起。见戈朗。org/issue/15329。
			o.init(call)
			o.call(call)
			if lhs == nil || lhs.Op() != ir.ONAME || base.Flag.Cfg.Instrumenting {
				return o.copyExpr(n)
			}
		} else {
			n.X = o.expr(n.X, nil)
		}
		return n

	case ir.OANDAND, ir.OOROR:
		// /…=LHS和RHS 
		// 
		// var bool 
		// r=LHS 
		// /如果r{
		// r=RHS 
		// /}
		// /r 

		n := n.(*ir.LogicalExpr)
		r := o.newTemp(n.Type(), false)

		// 评估左侧。
		lhs := o.expr(n.X, nil)
		o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, lhs)))

		// 计算右侧，保存生成的代码。
		saveout := o.out
		o.out = nil
		t := o.markTemp()
		o.edge()
		rhs := o.expr(n.Y, nil)
		o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, rhs)))
		o.cleanTemp(t)
		gen := o.out
		o.out = saveout

		// 如果左侧未导致短路，则向右侧发出。
		nif := ir.NewIfStmt(base.Pos, r, nil, nil)
		if n.Op() == ir.OANDAND {
			nif.Body = gen
		} else {
			nif.Else = gen
		}
		o.out = append(o.out, nif)
		return r

	case ir.OCALLMETH:
		base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
		panic("unreachable")

	case ir.OCALLFUNC,
		ir.OCALLINTER,
		ir.OCAP,
		ir.OCOMPLEX,
		ir.OCOPY,
		ir.OIMAG,
		ir.OLEN,
		ir.OMAKECHAN,
		ir.OMAKEMAP,
		ir.OMAKESLICE,
		ir.OMAKESLICECOPY,
		ir.ONEW,
		ir.OREAL,
		ir.ORECOVERFP,
		ir.OSTR2BYTES,
		ir.OSTR2BYTESTMP,
		ir.OSTR2RUNES:

		if isRuneCount(n) {
			// len（[]符文）被重写为运行时。稍后再计数。
			conv := n.(*ir.UnaryExpr).X.(*ir.ConvExpr)
			conv.X = o.expr(conv.X, nil)
		} else {
			o.call(n)
		}

		if lhs == nil || lhs.Op() != ir.ONAME || base.Flag.Cfg.Instrumenting {
			return o.copyExpr(n)
		}
		return n

	case ir.OINLCALL:
		n := n.(*ir.InlinedCallExpr)
		o.stmtList(n.Body)
		return n.SingleResult()

	case ir.OAPPEND:
		// 检查附加（x，make（[]T，y）…）。
		n := n.(*ir.CallExpr)
		if isAppendOfMake(n) {
			n.Args[0] = o.expr(n.Args[0], nil) // 顺序x 
			mk := n.Args[1].(*ir.MakeExpr)
			mk.Len = o.expr(mk.Len, nil) // 顺序y 
		} else {
			o.exprList(n.Args)
		}

		if lhs == nil || lhs.Op() != ir.ONAME && !ir.SameSafeExpr(lhs, n.Args[0]) {
			return o.copyExpr(n)
		}
		return n

	case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR:
		n := n.(*ir.SliceExpr)
		n.X = o.expr(n.X, nil)
		n.Low = o.cheapExpr(o.expr(n.Low, nil))
		n.High = o.cheapExpr(o.expr(n.High, nil))
		n.Max = o.cheapExpr(o.expr(n.Max, nil))
		if lhs == nil || lhs.Op() != ir.ONAME && !ir.SameSafeExpr(lhs, n.X) {
			return o.copyExpr(n)
		}
		return n

	case ir.OCLOSURE:
		n := n.(*ir.ClosureExpr)
		if n.Transient() && len(n.Func.ClosureVars) > 0 {
			n.Prealloc = o.newTemp(typecheck.ClosureType(n), false)
		}
		return n

	case ir.OMETHVALUE:
		n := n.(*ir.SelectorExpr)
		n.X = o.expr(n.X, nil)
		if n.Transient() {
			t := typecheck.MethodValueType(n)
			n.Prealloc = o.newTemp(t, false)
		}
		return n

	case ir.OSLICELIT:
		n := n.(*ir.CompLitExpr)
		o.exprList(n.List)
		if n.Transient() {
			t := types.NewArray(n.Type().Elem(), n.Len)
			n.Prealloc = o.newTemp(t, false)
		}
		return n

	case ir.ODOTTYPE, ir.ODOTTYPE2:
		n := n.(*ir.TypeAssertExpr)
		n.X = o.expr(n.X, nil)
		if !types.IsDirectIface(n.Type()) || base.Flag.Cfg.Instrumenting {
			return o.copyExprClear(n)
		}
		return n

	case ir.ORECV:
		n := n.(*ir.UnaryExpr)
		n.X = o.expr(n.X, nil)
		return o.copyExprClear(n)

	case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
		n := n.(*ir.BinaryExpr)
		n.X = o.expr(n.X, nil)
		n.Y = o.expr(n.Y, nil)

		t := n.X.Type()
		switch {
		case t.IsString():
			// 在转换期间标记字符串（字节片）参数以重用支持
			// 缓冲区的字节片。字符串比较不
			// 记住字符串以备将来使用，因此它是安全的。
			if n.X.Op() == ir.OBYTES2STR {
				n.X.(*ir.ConvExpr).SetOp(ir.OBYTES2STRTMP)
			}
			if n.Y.Op() == ir.OBYTES2STR {
				n.Y.(*ir.ConvExpr).SetOp(ir.OBYTES2STRTMP)
			}

		case t.IsStruct() || t.IsArray():
			// 对于复杂的比较，我们需要两个参数都是
			// 可寻址的，这样我们就可以将它们传递给运行时。
			n.X = o.addrTemp(n.X)
			n.Y = o.addrTemp(n.Y)
		}
		return n

	case ir.OMAPLIT:
		// map[int]int{
		// a（）：b（），
		// c（）：d（），
		// }
		// 到
		// m:=map[int]int}int{}
		// 然后对结果排序。
		// 如果没有这个特殊情况，order将在将任何键和值存储到地图之前计算所有
		// 键和值。
		// 见第26552期。
		n := n.(*ir.CompLitExpr)
		entries := n.List
		statics := entries[:0]
		var dynamics []*ir.KeyExpr
		for _, r := range entries {
			r := r.(*ir.KeyExpr)

			if !isStaticCompositeLiteral(r.Key) || !isStaticCompositeLiteral(r.Value) {
				dynamics = append(dynamics, r)
				continue
			}

			// 对一些静态条目进行递归排序可以将它们更改为动态；
			// 例如，OConvidace节点。见#31777。
			r = o.expr(r, nil).(*ir.KeyExpr)
			if !isStaticCompositeLiteral(r.Key) || !isStaticCompositeLiteral(r.Value) {
				dynamics = append(dynamics, r)
				continue
			}

			statics = append(statics, r)
		}
		n.List = statics

		if len(dynamics) == 0 {
			return n
		}

		// 发出地图的创建（及其所有静态条目）。
		m := o.newTemp(n.Type(), false)
		as := ir.NewAssignStmt(base.Pos, m, n)
		typecheck.Stmt(as)
		o.stmt(as)

		// Emit eval+插入动态条目，一次一个。
		for _, r := range dynamics {
			as := ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, r.Key), r.Value)
			typecheck.Stmt(as) // 注意：这会将OINDEX转换为OINDEXMAP 
			o.stmt(as)
		}
		return m
	}

	// 上面没有返回类型的断言。每个案例都必须回归自身。
}

// as2func命令OAS2FUNC节点。它创建临时表以确保从左到右的分配。
// 调用方在调用order之前，应该先订购任务的右侧。as2func。
// 它重写了，
// a，b，a=。。。
// as 
// tmp1，tmp2，tmp3=。。。
// a，b，a=tmp1，tmp2，tmp3 
// 这是确保从左到右分配顺序所必需的。
func (o *orderState) as2func(n *ir.AssignListStmt) {
	results := n.Rhs[0].Type()
	as := ir.NewAssignListStmt(n.Pos(), ir.OAS2, nil, nil)
	for i, nl := range n.Lhs {
		if !ir.IsBlank(nl) {
			typ := results.Field(i).Type
			tmp := o.newTemp(typ, typ.HasPointers())
			n.Lhs[i] = tmp
			as.Lhs = append(as.Lhs, nl)
			as.Rhs = append(as.Rhs, tmp)
		}
	}

	o.out = append(o.out, n)
	o.stmt(typecheck.Stmt(as))
}

// as2ok通过ok订购OAS2XXX。
// 与as2func一样，这也添加了临时变量，以确保从左到右的分配。
func (o *orderState) as2ok(n *ir.AssignListStmt) {
	as := ir.NewAssignListStmt(n.Pos(), ir.OAS2, nil, nil)

	do := func(i int, typ *types.Type) {
		if nl := n.Lhs[i]; !ir.IsBlank(nl) {
			var tmp ir.Node = o.newTemp(typ, typ.HasPointers())
			n.Lhs[i] = tmp
			as.Lhs = append(as.Lhs, nl)
			if i == 1 {
				// 根据Go 
				// 规范，“ok”结果是一个非类型化的布尔值。我们需要显式地将其转换为
				// 中的LHS类型，如果后者是定义的布尔值类型（#8475）。
				tmp = typecheck.Conv(tmp, nl.Type())
			}
			as.Rhs = append(as.Rhs, tmp)
		}
	}

	do(0, n.Rhs[0].Type())
	do(1, types.Types[types.TBOOL])

	o.out = append(o.out, n)
	o.stmt(typecheck.Stmt(as))
}

// isfuncpcinquired返回n是否是internal/abi的直接调用。FuncPCABIxxx函数。
func isFuncPCIntrinsic(n *ir.CallExpr) bool {
	if n.Op() != ir.OCALLFUNC || n.X.Op() != ir.ONAME {
		return false
	}
	fn := n.X.(*ir.Name).Sym()
	return (fn.Name == "FuncPCABI0" || fn.Name == "FuncPCABIInternal") &&
		(fn.Pkg.Path == "internal/abi" || fn.Pkg == types.LocalPkg && base.Ctxt.Pkgpath == "internal/abi")
}

// isIfaceOfFunc返回n是否是从func的直接引用进行的接口转换。
func isIfaceOfFunc(n ir.Node) bool {
	return n.Op() == ir.OCONVIFACE && n.(*ir.ConvExpr).X.Op() == ir.ONAME && n.(*ir.ConvExpr).X.(*ir.Name).Class == ir.PFUNC
}
